// Copyright (c) 2002 Torvald Riegel. All rights reserved.
// This program is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2, or (at your option)
// any later version.

package cpne;


import javax.swing.*;
import javax.swing.event.*;
import javax.swing.text.*;
import javax.swing.tree.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import org.tigris.gef.base.*;
import org.tigris.gef.event.*;
import org.tigris.gef.graph.presentation.*;
import org.tigris.gef.presentation.*;

public class AppFrame extends JGraphFrame
{
    //protected JPanel _propPanel = new JPanel(new BorderLayout());
    protected JSplitPane mainsp;

    public AppFrame(String title)
        {
            super(title);
            // replace the graph panel with a SplitPane
            _mainPanel.remove(_graphPanel);
            mainsp = new JSplitPane(JSplitPane.VERTICAL_SPLIT, false,
                                    _graphPanel,
                                    ui_propNoSingleSelection());
            // 1.4 jdk docs say this setting the dividerlocation wont work
            // if the splitpane is not on screen yet
            //mainsp.setDividerLocation(0.5);
            mainsp.setResizeWeight(0.5); // minimumSize is too small for JGraph
            mainsp.setOneTouchExpandable(true);
            _mainPanel.add(mainsp, BorderLayout.CENTER);
            // init more ...
            setToolBar(new AppPalette());
            // delete key results in dispose(), not delete(), FIXME
            // why doesnt it work ?
            getGraph().bindKey(new CmdDispose(), KeyEvent.VK_DELETE, 0);

            // update property pane if selection changes
            getGraph().addGraphSelectionListener(new GraphSelectionListener()
                {
                    public void selectionChanged(GraphSelectionEvent gse)
                        {
                            JComponent jc = null;
                            if (gse.getSelections().size() == 1) 
                            {
                                Object o = ((Fig)gse.getSelections().get(0)).
                                    getOwner();
                                try
                                {
                                    if (Class.forName("cpne.TPEdge").
                                        isInstance(o))
                                        jc = ui_propTPEdge((TPEdge)o);
                                    if (Class.forName("cpne.PTEdge").
                                        isInstance(o))
                                        jc = ui_propPTEdge((PTEdge)o);
                                    if (Class.forName("cpne.PlaceNode").
                                        isInstance(o))
                                        jc = ui_propPlaceNode((PlaceNode)o);
                                    if (Class.forName("cpne.TransitionNode").
                                        isInstance(o))
                                        jc = ui_propTransitionNode
                                            ((TransitionNode)o);
                                }
                                catch (java.lang.ClassNotFoundException e) {}
                            }
                            if (jc == null) jc = ui_propNoSingleSelection();
                            int prevLoc = mainsp.getDividerLocation();
                            mainsp.setBottomComponent(jc);
                            mainsp.setDividerLocation(prevLoc);
                            mainsp.getBottomComponent().validate();
                        }
                } );
        }
 
    ///////////////////////////////////////////////////////////////////////
    // UI pieces
    protected JComponent ui_propNoSingleSelection() 
        {
            return new JLabel("select one element to edit its properties");
        }
    
    protected JComponent ui_propTPEdge(TPEdge edge) 
        {
            return ui_exprEditPane("Expression", edge.getExprDocument());
        }
    protected JComponent ui_propPTEdge(PTEdge edge) 
        {
            JPanel p = new JPanel(new BorderLayout());
            JPanel pcb = new JPanel(new BorderLayout());
            pcb.add(new JLabel("Type:"), BorderLayout.WEST);
            JComboBox cb = new JComboBox(edge.getTypeModel());
            pcb.add(cb);
            p.add(pcb, BorderLayout.NORTH);
            p.add(ui_exprEditPane("Expression", edge.getExprDocument()),
                  BorderLayout.CENTER);
            return p;
        }

    //////////////////////////////////////////////////////////////////////
    //
    // editing places (tokens)
    //
    public class TokenTreeHandler
        implements ActionListener, TreeSelectionListener
    {
        TokenTreeModel tm;
        JTree tree;
        int selectedTokenIndex;
        JTextArea tokenTextArea;

        public TokenTreeHandler(JTree tree2, JTextArea ta)
            {
                tree = tree2;
                tm = (TokenTreeModel)tree.getModel();
                // init the token's text area with a dummy document
                tokenTextArea = ta;
                setEmptySelectionDocument();
            }
        public void setEmptySelectionDocument()
            {
                tokenTextArea.setDocument(
                    new PerlExprDocument("select a node to edit it"));
            }
        /**
         * Set the text area's document to the one of the currently selected
         * token.
         */
        public void valueChanged(TreeSelectionEvent e)
            {
                TreePath path = tree.getSelectionPath();
                if (path == null || path.getPathCount() < 2)
                {
                    setEmptySelectionDocument();
                    selectedTokenIndex = -1;
                }
                else 
                {
                    selectedTokenIndex =
                        ((Integer)path.getPathComponent(1)).intValue();
                    tokenTextArea.setDocument(tm.getToken(selectedTokenIndex));
                }
            }       
        public void actionPerformed(ActionEvent e)
            {
                String cmd = e.getActionCommand();
                if (cmd.equals("add"))
                {
                    // insert empty node at the last position
                    int index = tm.addToken();
                    // selects the new node
                    tree.setSelectionPath(
                        new TreePath(
                            new Object[] { tm.getRoot(),
                                           tm.getTokenIndexContainer(index)})
                        );
                    // expandPath doesnt work if there are no children yet =>
                    if (tm.getChildCount(tm.getRoot()) == 1)
                        tree.expandPath(new TreePath(tm.getRoot()));
                }
                else if (cmd.equals("remove") && selectedTokenIndex != -1)
                {
                    // gets the current node index
                    int tmIndex = tm.getIndexOfChild(
                        tm.getRoot(), tm.getTokenIndexContainer(
                            selectedTokenIndex));
                    // removes the token, possibly changing selectedTokenIndex
                    tm.removeToken(selectedTokenIndex);
                    // updates selection, try next/previous sibling
                    if (tm.getChildCount(tm.getRoot()) <= tmIndex) tmIndex--;
                    if (tmIndex >= 0)
                    {
                        tree.setSelectionPath(
                            new TreePath(new Object[]
                                { tm.getRoot(), tm.getChild(tm.getRoot(),
                                                            tmIndex) }));
                    }
                    else 
                    {
                        // clears any selection because there are no siblings.
                        // FIXME is this really needed or will the
                        // JTree default to an empty selection ?
                        // FIXME anyway, no valueChanged(treesel) is received!
                        tree.clearSelection();
                    }
                }
                else if (cmd.equals("inc") && selectedTokenIndex != -1)
                {
                    selectedTokenIndex = tm.move(selectedTokenIndex, +1);
                }
                else if (cmd.equals("dec") && selectedTokenIndex != -1)
                {
                    selectedTokenIndex = tm.move(selectedTokenIndex, -1);
                }
            }
                
    }

    public class TokenTreeCellRenderer extends DefaultTreeCellRenderer
    {
        public Component getTreeCellRendererComponent(
            JTree tree, Object value, boolean sel, boolean expanded,
            boolean leaf, int row, boolean hasFocus)
            {
                TokenTreeModel tm = (TokenTreeModel)tree.getModel();
                if ( value == tm.getRoot() || !leaf )
                    return super.getTreeCellRendererComponent(
                        tree, value, sel, expanded, leaf, row, hasFocus);
                // for non-root leaves a JTextArea is used
                JTextArea ta = new JTextArea((PerlExprDocument)value);
                /*if (hasFocus) 
                {
                    System.out.println("ha!");
                    ta.setRequestFocusEnabled(true);
                    ta.requestFocus();
                    ta.grabFocus();
                }*/
                return ta;
            }
    }
    /**
     * Creates and returns the component used for place properties.
     */
    protected JComponent ui_propPlaceNode(PlaceNode node) 
        {
            // the text area used to edit tokens
            JTextArea ta = new JTextArea();
            // Tree with custom renderer, invisible root node and
            // at most one selected node
            TokenTreeModel treeModel = node.getTokenModel();
            JTree tree = new JTree(treeModel);
            tree.putClientProperty("JTree.lineStyle", "Horizontal");
            DefaultTreeCellRenderer tcr = new TokenTreeCellRenderer();
            tcr.setLeafIcon(null);
            tree.setCellRenderer(tcr);
            tree.setRootVisible(false);
            tree.expandPath(new TreePath(treeModel.getRoot()));
            tree.getSelectionModel().setSelectionMode(
                TreeSelectionModel.SINGLE_TREE_SELECTION);
            // event handler for the token tree tth
            TokenTreeHandler tth = new TokenTreeHandler(tree, ta);
            tree.addTreeSelectionListener(tth);
            // Buttons on button panel bp
            JButton ba = new JButton("Add");
            ba.setActionCommand("add");
            ba.setHorizontalTextPosition(AbstractButton.CENTER);
            ba.addActionListener(tth);
            JButton br = new JButton("Remove");
            br.setActionCommand("remove");
            br.setHorizontalTextPosition(AbstractButton.CENTER);
            br.addActionListener(tth);
            JButton bi = new JButton("Inc");
            bi.setActionCommand("inc");
            bi.setHorizontalTextPosition(AbstractButton.CENTER);
            bi.addActionListener(tth);
            JButton bd = new JButton("Dec");
            bd.setActionCommand("dec");
            bd.setHorizontalTextPosition(AbstractButton.CENTER);
            bd.addActionListener(tth);
            JPanel bp = new JPanel(new GridLayout(1,4));
            bp.add(ba);
            bp.add(br);
            bp.add(bi);
            bp.add(bd);
            // buttons + textarea on edit panel
            JPanel ep = new JPanel(new BorderLayout());
            ep.add(bp, BorderLayout.NORTH);
            ep.add(new JScrollPane(ta));
            // both together
            JSplitPane all = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,
                                            false,
                                            new JScrollPane(tree),
                                            ep);
            all.setDividerLocation(0.5);
            all.setResizeWeight(0.5);
            all.setOneTouchExpandable(true);
            all.setBorder(BorderFactory.createTitledBorder("Tokens"));
            return all;
        }
    //
    // transition nodes
    //
    protected JComponent ui_propTransitionNode(TransitionNode node) 
        {
            JSplitPane spr = new JSplitPane
                (JSplitPane.VERTICAL_SPLIT, false,
                 ui_exprEditPane("Guard", node.getGuardDocument()),
                 ui_exprEditPane("Code", node.getCodeDocument()) );
            spr.setDividerLocation(0.5);
            spr.setResizeWeight(0.5);
            spr.setOneTouchExpandable(true);
            return spr;
        }

    protected JComponent ui_exprEditPane(String title, Document doc)
        {
            JScrollPane sc = new JScrollPane(new JTextArea(doc));
            sc.setBorder(BorderFactory.createTitledBorder(title));
            return sc;
        }
    
}

